package com.serialPort.web.service.impl;

import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortDataListener;
import com.fazecast.jSerialComm.SerialPortEvent;
import com.serialPort.web.domain.*;
import com.serialPort.web.service.PostTbLisJybgmxzd;
import com.serialPort.web.service.SerialPortService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
@Service
public class SerialPortServiceImpl implements SerialPortService {

    @Value("${lisapi.sleepTime}")
    Integer sleepTime;

    @Value("${lisapi.com}")
    String COM;

    @Value("${lisapi.baudrate}")
    String baudrate;


    @Autowired
    PostTbLisJybgmxzd postTbLisJybgmxzd;

    private ReentrantLock lock = new ReentrantLock();
    private long lastReadTime = System.currentTimeMillis();
    private Timer timeoutTimer;
    private StringBuilder totalMessage = new StringBuilder();

    /**
     * 打开串口并发送数据
     *
     * @param message
     */
    @Override
    public void SendToSerialPortMessage(SerialPort serialPort, String message) {
        serialPort.setBaudRate(Integer.parseInt(baudrate));
        if (serialPort.openPort()) {
            log.info("端口已开放！发送数据：" + message);
            serialPort.writeBytes(message.getBytes(), message.length());
        } else {
            log.info("打开端口失败！！！");
        }
    }

    /**
     * 监听端口并获取数据
     *
     * @param serialPort
     */
    @Override
    public void GetSerialPortMessage(SerialPort serialPort) {
        // 使用非阻塞模式，确保及时响应握手信号
        serialPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 100, 0);
        serialPort.setParity(SerialPort.EVEN_PARITY);//偶校验
        serialPort.setFlowControl(SerialPort.FLOW_CONTROL_DISABLED);//关闭流控
        //是否开启串口
        if (serialPort.openPort()) {

            serialPort.addDataListener(new SerialPortDataListener() {
                @Override
                public int getListeningEvents() {
                    return SerialPort.LISTENING_EVENT_DATA_RECEIVED;//返回要监听的事件类型，以供回调函数使用。可发回的事件包括：SerialPort.LISTENING_EVENT_DATA_AVAILABLE，SerialPort.LISTENING_EVENT_DATA_WRITTEN,SerialPort.LISTENING_EVENT_DATA_RECEIVED。分别对应有数据在串口（不论是读的还是写的），有数据写入串口，从串口读取数据。如果AVAILABLE和RECEIVED同时被监听，优先触发RECEIVED
                }

                @Override
                public void serialEvent(SerialPortEvent event) {
                    if (event.getEventType() != SerialPort.LISTENING_EVENT_DATA_RECEIVED) {
                        return;//判断事件的类型
                    }

                    try {
                        // 添加延迟，确保数据完全到达
//                        Thread.sleep(50);

                        byte[] newData = event.getReceivedData();
                        if (newData != null && newData.length > 0) {
                            // 同时打印十六进制格式，便于调试
                            log.info("十六进制数据: " + bytesToHex(newData));

                            // 检查是否是ENQ握手信号
                            if (newData.length == 1 && newData[0] == 0x05) {
                                log.info("✅ 接收到ENQ握手信号，准备响应ACK");
                                respondToENQ(serialPort);
                                return;
                            }
                            // 检查是否是HL7消息开始（可能包含STX）
                            if (isHL7MessageStart(newData)) {
                                log.info("✅ 接收到HL7消息数据");
                                String message = extractHL7Message(newData);

                                lock.lock();
                                try {
                                    totalMessage.append(message);
                                    lastReadTime = System.currentTimeMillis();
                                    resetTimeoutTimer(serialPort);
                                    log.info("当前累积消息长度: {}", totalMessage.length());
                                } finally {
                                    lock.unlock();
                                }
                            }

//                            // 指定正确的字符编码
//                            String message = new String(newData, StandardCharsets.UTF_8);
//
//                            log.info(serialPort.getSystemPortName() + "接收到数据: \n" + message);
//
//
//
//                            lock.lock();
//                            try {
//                                totalMessage.append(message);
//                                lastReadTime = System.currentTimeMillis();
//                                resetTimeoutTimer(serialPort);
//                            } finally {
//                                lock.unlock();
//                            }
                        } else {
                            log.warn("接收到空数据或数据长度为0");
                        }
                    } catch (Exception e) {
                        log.error("处理串口数据时发生错误", e);
                    }

                }
            });

        }
    }

    // 字节数组转十六进制字符串工具方法
    private String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X ", b));
        }
        return sb.toString();
    }

    /**
     * 解析CS1200消息
     *
     * @param message
     */
    @Override
    public List<List<String>> AnalysisCS1200Message(String message) {
        if (message == null || message.isEmpty()) return null;
        List<List<String>> msh_list = new ArrayList<>();
        try {
            message = message.replace("<SB>", "").replace("<EB>", "").replace("\r", "\n").replace("<CR>", "\n");
            String[] message_cr_list = message.split("\n");
            List<String> str_List = new ArrayList<>();
            for (String str : message_cr_list) {
                if (!str.isEmpty()) {
                    str_List.add(str);
                }
            }

            int startIndex = 0;
            int endIndex = 0;
            for (int i = 0; i < str_List.size(); i++) {
                if (str_List.get(i).contains("H|\\^&|") || i == str_List.size() - 1) {
                    endIndex = i;
                    if ((endIndex - startIndex) > 0) {
                        msh_list.add(str_List.subList(startIndex, i == str_List.size() - 1 ? str_List.size() : endIndex));
                        startIndex = i;
                    }
                }
            }
            log.info("解析后数据：" + msh_list);
        } catch (Exception e) {
            log.error("解析数据方法报错：" + e);
            throw new RuntimeException("解析数据方法报错：" + e);
        }
        return msh_list;
    }

    /**
     * 解析并发送lis
     * @param message
     * @return
     */
    @Override
    public String AnalysisUploadCS1200Message(String message) {
        String result_code = "200";
        if (message == null || message.isEmpty() || message.replace("\n", "").isEmpty()) {
            return "500";
        }

        List<List<String>> analysisList = AnalysisCS1200Message(message);   //解析后list列表每条为一组数据
        if (analysisList == null || analysisList.isEmpty()) {
            return "500";
        }


        int len = 0;
        for (List<String> messageList : analysisList) {
            HL7Message hl7Message = cs1200MessageFormate(messageList);
            if (hl7Message == null) continue;
            Object code_obj = postTbLisJybgmxzd.uploadLis(hl7Message);
            if (code_obj == null || !"200".equals(String.valueOf(code_obj))) {
                result_code = "500";
                len = len + 1;
            }
        }

        return result_code;
    }


    /**
     * 重置超时时间
     */
    private void resetTimeoutTimer(SerialPort serialPort) {
        if (timeoutTimer != null) {
            timeoutTimer.cancel();
            timeoutTimer.purge();
        }

        timeoutTimer = new Timer();
        timeoutTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                String reuturnMessage = checkAndProcessMessage();
                SendToSerialPortMessage(serialPort, reuturnMessage);
            }
        }, sleepTime);
    }

    /**
     * 检测消息队列
     */
    private String checkAndProcessMessage() {
        lock.lock();
        try {
            long currentTime = System.currentTimeMillis();
            if (currentTime - lastReadTime >= sleepTime && totalMessage.length() > 0) {
                String completeMessage = totalMessage.toString();
                totalMessage.setLength(0);
                log.info("超时" + sleepTime + "ms触发，处理完整消息: " + completeMessage);
                List<List<String>> analysisList = AnalysisCS1200Message(completeMessage);   //解析后list列表每条为一组数据
                if (analysisList == null || analysisList.size() == 0) {
                    return null;
                }

                String result_code = "200";
                int len = 0;
                HL7Message returnHl7Message = new HL7Message();
                for (List<String> messageList : analysisList) {
                    HL7Message hl7Message = cs1200MessageFormate(messageList);
                    returnHl7Message = hl7Message;
                    if (hl7Message == null) continue;
                    Object code_obj = postTbLisJybgmxzd.uploadLis(hl7Message);
                    if (code_obj == null || !"200".equals(String.valueOf(code_obj))) {
                        result_code = "500";
                        len = len + 1;
                    }
                }

                if ("200".equals(result_code)) {
                    return handleBuildReturnMessage(returnHl7Message, "success", null);
                } else {
                    return handleBuildReturnMessage(returnHl7Message, "error", len + " data was faild!");
                }

            }
        } finally {
            lock.unlock();
        }
        return null;
    }



    /**
     * 格式化数据返回对象
     */
    public HL7Message cs1200MessageFormate(List<String> messageList) {
        if (messageList == null || messageList.size() == 0) return null;
        HL7Message hl7Message = new HL7Message();
        SystemInfo systemInfo = new SystemInfo();
        PatientInfo patientInfo = new PatientInfo();
        SampleInfo sampleInfo = new SampleInfo();
        List<ObxInfo> obxInfoList = new ArrayList<>();
        int MSH_index = 0;
        int PID_index = MSH_index + 1;
        int OBR_index = PID_index + 1;
        int OBX_index = OBR_index + 1;
        int i = 0;
        for (String line : messageList) {
            //MSH行
            if (line.contains("H|\\^&") || line.contains("|Host|")) {
                String[] line_splitByLine = line.split("\\|", -1);
                systemInfo.setLisId(line_splitByLine[4]);
                systemInfo.setSystemTime(line_splitByLine[12]);
                MSH_index = i;
                PID_index = MSH_index + 1;
                OBR_index = PID_index + 1;
                OBX_index = OBR_index + 1;
            }

            //PID行
            if (line.contains("P|") && i == PID_index) {
                String[] line_splitByLine = line.split("\\|", -1);
                patientInfo.setId(Integer.valueOf(line_splitByLine[1]));
                if (line_splitByLine.length > 5) {
                    patientInfo.setName(line_splitByLine[5]);
                }
                if (line_splitByLine.length > 8) patientInfo.setSex(line_splitByLine[8]);
            }

            //OBR行
            if (line.contains("O|") && i == OBR_index) {
                String[] line_splitByLine = line.split("\\|", -1);
                sampleInfo.setSampleId(line_splitByLine[1]);
                sampleInfo.setCodeNum(line_splitByLine[2].split("\\^",-1)[1]);
//                if (line_splitByLine.length > 3) sampleInfo.setSampleId(line_splitByLine[3]);
//                if (line_splitByLine.length > 4) sampleInfo.setName(line_splitByLine[4]);
                if (line_splitByLine.length > 6) sampleInfo.setProvTime(line_splitByLine[6]);
//                if (line_splitByLine.length > 15) sampleInfo.setSampleType(line_splitByLine[15]);
            }

            //OBX行
            if (line.contains("R|") && i >= OBX_index) {
                String[] line_splitByLine = line.split("\\|", -1);
                ObxInfo obxInfo = new ObxInfo();
//                obxInfo.setType(line_splitByLine[1]);
                obxInfo.setProcId(line_splitByLine[1]);
                if (line_splitByLine.length > 2) {
                    String[] sprocNameArray = line_splitByLine[2].split("\\^", -1);
                    obxInfo.setProcName(sprocNameArray.length == 2 ? sprocNameArray[1] : sprocNameArray.length == 4 ? sprocNameArray[3] : line_splitByLine[2]);
                }
                if (line_splitByLine.length > 3) obxInfo.setResult(line_splitByLine[3]);
                if (line_splitByLine.length > 4) obxInfo.setUnit(line_splitByLine[4]);
                if (line_splitByLine.length > 5) obxInfo.setReferenceRange(line_splitByLine[5]);
                if (line_splitByLine.length > 12) obxInfo.setTime(line_splitByLine[12]);
                obxInfoList.add(obxInfo);
            }
            i += 1;
        }

        hl7Message.setSystemInfo(systemInfo);
        hl7Message.setPatientInfo(patientInfo);
        hl7Message.setSampleInfo(sampleInfo);
        hl7Message.setObxInfoList(obxInfoList);

        return hl7Message;
    }

    /**
     * 生成返回消息
     *
     * @param hl7Message
     * @param type       "success"返回成功，"error"返回失败
     * @return String 生成的消息内容
     */
    public String handleBuildReturnMessage(HL7Message hl7Message, String type, String errorMessage) {
        SimpleDateFormat dateTimeFormate = new SimpleDateFormat("yyyyMMddHHmmss");
        String formatdate = dateTimeFormate.format(new Date());
        String message = null;
        if ("success".equals(type)) {
            //成功返回
            message = "<SB>MSH|^~\\&|" + hl7Message.getSystemInfo().getMessageId() +
                    "|" + hl7Message.getSystemInfo().getMachineName() + "|" +
                    hl7Message.getSystemInfo().getLisId() + "||" + formatdate + "||ACK^R01|" +
                    hl7Message.getSystemInfo().getMessageId() + "|P|2.3.1||||0||" +
                    hl7Message.getSystemInfo().getStrCode() + "|||<CR>" +
                    "MSA|AA|1||||0|<CR>" +
                    "<EB><CR>";
        } else {
            //失败返回
            message = "<SB>MSH|^~\\&|" + hl7Message.getSystemInfo().getMessageId() +
                    "|" + hl7Message.getSystemInfo().getMachineName() + "|" +
                    hl7Message.getSystemInfo().getLisId() + "||" + formatdate + "||ACK^R01|" +
                    hl7Message.getSystemInfo().getMessageId() + "|P|2.3.1||||0||" +
                    hl7Message.getSystemInfo().getStrCode() + "|||<CR>" +
                    "MSA|AR|1|" + errorMessage + ". please looked logs|||206|<CR>" +
                    "<EB><CR>";
        }
        return message;
    }

    /**
     * 响应ENQ握手信号
     */
    private void respondToENQ(SerialPort serialPort) {
        try {
            // HL7协议要求响应ACK (0x06)
            byte[] ackResponse = new byte[] { 0x06 };
            serialPort.writeBytes(ackResponse, ackResponse.length);
            log.info("✅ 已发送ACK响应: {}", bytesToHex(ackResponse));

//            // 可选：发送就绪信号
//            Thread.sleep(100);
//            byte[] readySignal = "READY\r\n".getBytes(StandardCharsets.US_ASCII);
//            serialPort.writeBytes(readySignal, readySignal.length);
//            log.info("✅ 已发送READY信号");

        } catch (Exception e) {
            log.error("响应ENQ时发生错误", e);
        }
    }

    /**
     * 检查是否是HL7消息开始
     */
    private boolean isHL7MessageStart(byte[] data) {
        if (data == null || data.length == 0) return false;

        // 检查是否包含STX (0x02) 或 HL7消息头
        String message = new String(data, StandardCharsets.UTF_8);
        return message.contains("MSH|") || (data.length > 0 && data[0] == 0x02);
    }

    /**
     * 提取HL7消息内容
     */
    private String extractHL7Message(byte[] data) {
        String message = new String(data, StandardCharsets.UTF_8);

        // 如果消息以STX开始，去除STX和ETX
        if (data.length > 0 && data[0] == 0x02) {
            // 查找ETX (0x03)
            for (int i = 1; i < data.length; i++) {
                if (data[i] == 0x03) {
                    message = new String(data, 1, i - 1, StandardCharsets.UTF_8);
                    break;
                }
            }
        }

        return message;
    }

}
